#ifndef STORAGE_CONFIG_H
#define STORAGE_CONFIG_H

#include <muduo/base/Types.h>
#include <muduo/base/Logging.h>
#include <muduo/net/InetAddress.h>
#include <fstream>
#include <assert.h>
#include <pthread.h>
#include <netinet/in.h>
#include <vector>
#include <arpa/inet.h>
#include <stdint.h>
#include <endian.h>

#include <boost/noncopyable.hpp>

#include <sdfs/config/Json.h>
#include <sdfs/common/ToolKit.h>

using namespace muduo;

namespace sdfs
{
class StorageConfig: public boost::noncopyable
{
public:

	typedef boost::shared_ptr<StorageConfig> StorageConfigPtr;

	static StorageConfig& getInstance()
	{
		pthread_once(&ponce_, &StorageConfig::parseConfg);
		return *instance_;
	}

	static void setConfigFilename(const muduo::string &filename)
	{
		assert(sdfs::ToolKit::getCfgFileName(confname_, filename) == true);
	}
	

	static void flush()
	{
		sdfs::JsonHelper::SerilizeJson(confname_, *tree_, true);
	}

	~StorageConfig(){}
private:

	StorageConfig()
	{
	}

	

	static void parseConfg()
	{
		assert(confname_.length() != 0);
		instance_ = new StorageConfig();
		tree_ = sdfs::JsonHelper::ParseJson(confname_);
		assert(tree_ != NULL);
		instance_->name_ = (*tree_)["name"].asString().c_str();
		instance_->filePort_ = (*tree_)["filePort"].asInt();
		instance_->configPort_ = (*tree_)["configPort"].asInt();
		instance_->ip_ = (*tree_)["ip"].asString().c_str();
		instance_->gourpIdx_ = (*tree_)["groupIdx"].asInt();
		instance_->storageCapacityMB_ = (*tree_)["storageCapacityMB"].asInt();
		instance_->storageDir_ = (*tree_)["storageDir"].asString().c_str();
		instance_->blockSizeMB_ = (*tree_)["blockSizeMB"].asInt();
		instance_->blockNum_ = (*tree_)["blockNum"].asInt();
		instance_->blockReaderNum_ = (*tree_)["blockReaderNum"].asInt();
		instance_->threadNum_ = (*tree_)["threadNum"].asInt();
		instance_->syncIntervalMs_ = (*tree_)["syncIntervalMs"].asInt();
		instance_->lastsynctime_ = (*tree_)["lastsynctime"].asInt();
		Json::Value trackerRoot = (*tree_)["trackers"];
		for(uint32_t i = 0 ; i < trackerRoot.size() ; ++i)
		{
			const char* ip = trackerRoot[i]["ip"].asCString();
			int port = trackerRoot[i]["port"].asInt();
			/****************************************************************************\
			 struct  sockaddr_in {
			 short  int  sin_family;              
			 unsigned  short  int  sin_port; 
			 struct  in_addr  sin_addr;   
			 unsigned  char  sin_zero[8]; 
			 };
			\****************************************************************************/
			sockaddr_in sockaddr;
			sockaddr.sin_family = AF_INET;
			sockaddr.sin_port = htobe16(port);
			if (::inet_pton(AF_INET, ip, &sockaddr.sin_addr) <= 0)
			{
			    LOG_SYSERR << "sockets::fromIpPort";
			}
			muduo::net::InetAddress addr(sockaddr);
			instance_->trackers_.push_back(addr);
		}

		Json::Value redisRoot = (*tree_)["redis"];
		instance_->redisIp_ = redisRoot["ip"].asString().c_str();
		instance_->redisPort_ = redisRoot["port"].asInt();

		LOG_INFO << "server config: [" << name() << ":" << filePort() <<
			" configPort: " << configPort() <<
			" groupIdx: " << 
			" threadNum: " << threadNum();
		
	}
public:

	static int filePort()
	{
		return instance_->filePort_;
	}

	static int groupIdx()
	{
		return instance_->gourpIdx_;
	}

	static int configPort()
	{
		return instance_->configPort_;
	}

	static int storageCapacityMB()
	{
		return instance_->storageCapacityMB_;
	}

	static muduo::string& storageDir()
	{
		return instance_->storageDir_;
	}

	static const std::vector<muduo::net::InetAddress>* trackers()
	{
		return &(instance_->trackers_);
	}

	static int threadNum()
	{
		return instance_->threadNum_;
	}

	static muduo::string& name()
	{
		return instance_->name_;
	}

	static muduo::string& redisIp()
	{
		return instance_->redisIp_;
	}

	static int redisPort()
	{
		return instance_->redisPort_;
	}

	static string& ip()
	{
		return instance_->ip_;
	}

	static int blockNum()
	{
		return instance_->blockNum_;
	}

	static int blockSizeMB()
	{
		return instance_->blockSizeMB_;
	}

	static int blockReaderNum()
	{
		return instance_->blockReaderNum_;
	}

	static int syncIntervalMs()
	{
		return instance_->syncIntervalMs_;
	}

	static int lastSyncTime()
	{
		return instance_->lastsynctime_;
	}

	static void updateLastSyncTime(int time)
	{
		if(instance_->lastsynctime_ != time)
		{
			instance_->lastsynctime_ = time;
			flush();
		}
	}
private:
	muduo::string name_;
	int filePort_;
	int gourpIdx_;
	int configPort_;
	string ip_;
	int storageCapacityMB_;
	muduo::string storageDir_;
	int blockSizeMB_;
	int blockNum_;
	int blockReaderNum_;
	int threadNum_;
	muduo::string redisIp_;
	int redisPort_;
	int syncIntervalMs_;
	int lastsynctime_;

	std::vector<muduo::net::InetAddress> trackers_;

	static sdfs::JsonValuePtr tree_;

	static muduo::string confname_;

	static StorageConfig* instance_;
	static pthread_once_t ponce_;
};

pthread_once_t StorageConfig::ponce_ = PTHREAD_ONCE_INIT;
StorageConfig* StorageConfig::instance_ = NULL;
muduo::string StorageConfig::confname_ = "";
sdfs::JsonValuePtr StorageConfig::tree_;
}

#endif